package com.simple3d.loaders
{	
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.TriangleCulling;
	import flash.events.Event;
	import flash.geom.Matrix3D;
	import flash.geom.Utils3D;
	import flash.geom.Vector3D;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
		
	/**
	 * The ASE class lets you parse and load ASE files. Only single mesh parsing is done.
	 * @author Pradeek
	 * TODO : Add support for multiple meshes.
	 */		
	public class ASE extends Sprite
	{
		/**
		 *  Load Complete event. Dispatched when the ASE file is loaded and parsed.
		 *  Use this event to start rendering.
		 */		
		public static var LOAD_COMPLETE:String = "Load complete";
		
		private var displayMode:uint = 0;
		
		private var vertices:Array;
		private var faces:Array;
		private var tvertices:Array;
		private var tfaces:Array;
		
		protected var _data:String;
		protected var loader:URLLoader;
		protected var matrix3d:Matrix3D;
		protected var vertices3D:Vector.<Number>;
		protected var projectedVertices:Vector.<Number>;
		protected var indices:Vector.<int>;
		protected var uvtData:Vector.<Number>;
		
		/**
		 *  Color of the mesh. 
		 */		
		public var color:uint = 0x000000;
		
		/**
		 * BitmapData of the texture. 
		 */		
		public var texture:BitmapData;
		
		/**
		 * Indicates whether the model is loaded or not. 
		 */		
		public var isLoaded:Boolean = false;
		
		/**
		 * Constructor
		 * @param scaleFactor - Amount of scaling for the 3D object.
		 * @param texture - BitmapData of the texture. 
		 */		
		public function ASE(scaleFactor:Number = 1,texture:BitmapData = null)
		{
			this.texture = texture;
			
			if(texture)
			{
				displayMode = 1;
			}
			
			matrix3d = new Matrix3D();
						
			vertices = new Array();
			faces = new Array();
			tvertices = new Array();
			tfaces = new Array();
			
			vertices3D = new Vector.<Number>();
			projectedVertices = new Vector.<Number>();
			indices = new Vector.<int>();
			uvtData = new Vector.<Number>();
			
			matrix3d.appendScale(scaleFactor,scaleFactor,scaleFactor);
		}
		
		/**
		 * Loads the ASE file.
		 * @param url - the URL of the ASE file.
		 * 
		 */		
		public function loadFile(url:String):void
		{
			loader = new URLLoader();
			loader.dataFormat = URLLoaderDataFormat.TEXT;
			loader.addEventListener(Event.COMPLETE, onComplete);
			loader.load(new URLRequest(url));
		}
		
		private function onComplete(e:Event):void
		{
			_data = String(loader.data);
			parseData(_data);
			isLoaded = true;
			dispatchEvent(new Event(ASE.LOAD_COMPLETE));
		}
			
		protected function parseData(data:String):void
		{	
			var lines:Array = unescape(data).split('\r\n');
			
			var line:String;
			var chunk:String;
			var content:String;
			
			while(lines.length)	
			{
				line = String(lines.shift());
								
				line = line.substr(line.indexOf('*') + 1);
				
				if(line.indexOf('}') >= 0) line = '';
				
				chunk = line.substr(0, line.indexOf(' '));
				
				switch(chunk) 
				{
					case 'MESH_VERTEX_LIST':
						while((content = String(lines.shift())).indexOf('}') < 0) 
						{
							content = content.substr(content.indexOf('*') + 1);						
							var values:Array = content.split('\t');					
							
							vertices.push([parseFloat(values[1]), parseFloat(values[2]), parseFloat(values[3])]);
						}
						break;
					
					case 'MESH_FACE_LIST':
						while((content = String(lines.shift())).indexOf('}') < 0) 
						{
							content = content.substr(content.indexOf('*') + 1);
							
							var data:String = content.split('\t')[0];

							var indices:Array = data.split(':'); 
							
							var con:String;
							
							con = indices[2];
							var vIndex1:Number = parseInt(con.substr(0, con.lastIndexOf(' ')));
							con = indices[3];
							var vIndex2:Number = parseInt(con.substr(0, con.lastIndexOf(' ')));
							con = indices[4];
							var vIndex3:Number = parseInt(con.substr(0, con.lastIndexOf(' ')));
							
							faces.push([vIndex1, vIndex2, vIndex3]);
						}
						break;
						
					case 'MESH_TVERTLIST':
						while((content = String(lines.shift())).indexOf('}') < 0) 
						{
							content = content.substr(content.indexOf('*') + 1);
							var mtvl:Array = content.split('\t'); 
							
							tvertices.push([parseFloat(mtvl[1]), parseFloat(mtvl[2])]);
						}
						break;
					
					case 'MESH_TFACELIST':
						var num:Number = 0;
						while((content = String(lines.shift())).indexOf('}') < 0) 
						{
							content = content.substr(content.indexOf('*') + 1);
							var mtfl:Array = content.split('\t'); 
							
							tfaces.push([parseInt(mtfl[1]), parseInt(mtfl[2]), parseInt(mtfl[3])]);
						}
						break;						
				}
			}
			
			/* Texture Mapping */
			var n:int = -1;
			for (var a:uint = 0;a<faces.length;a++) 
			{
				for (var i:int = 0; i < faces[a].length; i++) 
				{
					var vertex:Array = vertices[faces[a][i]];
					vertices3D.push(vertex[0], vertex[1], vertex[2]); 
					n++;
					if(tfaces.length != 0)
					{
						var uv:Array = tvertices[tfaces[a][i]];
						uvtData.push(uv[0], uv[1], 1);
					}
				}
				indices.push(n-2, n-1, n);
			}
			
		}
		
		/**
		 *  Renders the 3D object.
		 *  Use the ASE.LOAD_COMPLETE event to start rendering.
		 */		
		public function render():void
		{	
			if(isLoaded)
			{		
				this.graphics.clear();
				
				Utils3D.projectVectors(matrix3d,vertices3D,projectedVertices,uvtData);
				
				switch(displayMode)
				{
					case 0:
						this.graphics.beginFill(color);
						this.graphics.lineStyle(1,color);
						this.graphics.drawTriangles(projectedVertices,indices);
						break;
					case 1:
						this.graphics.beginBitmapFill(this.texture,null,false,false);
						this.graphics.drawTriangles(projectedVertices,indices,uvtData,TriangleCulling.NEGATIVE);
						break;
				}
				
				this.graphics.endFill();
			}				
		}
		
		/**
		 *  Rotate the 3D object on all axes.
		 *  @param value - amount of rotation.
		 */		
		public function rotate(value:Number):void
		{
			matrix3d.appendRotation(value,Vector3D.X_AXIS);
			matrix3d.appendRotation(value,Vector3D.Y_AXIS);
			matrix3d.appendRotation(value,Vector3D.Z_AXIS);
		}				

		public override function set rotationX(value:Number):void
		{
			matrix3d.appendRotation(value,Vector3D.X_AXIS);
		}
		
		public override function set rotationY(value:Number):void
		{
			matrix3d.appendRotation(value,Vector3D.Y_AXIS);
		}
		
		public override function set rotationZ(value:Number):void
		{
			matrix3d.appendRotation(value,Vector3D.Z_AXIS);
		}
		
	}
}